<!DOCTYPE html>
<html lang="zh-cn">
<head>
   
    <link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=KOZafwuaDasEedEenI5aTy8aXH0epbm6VUJ0v3vsT_Q1"/>
<link id="MainCss" type="text/css" rel="stylesheet" href="/skins/ThinkInside/bundle-ThinkInside.css?v=RRjf6pEarGnbXZ86qxNycPfQivwSKWRa4heYLB15rVE1"/>
<link type="text/css" rel="stylesheet" href="/blog/customcss/428549.css?v=%2fam3bBTkW5NBWhBE%2fD0lcyJv5UM%3d"/>

</head>
<body>
<a name="top"></a>

<div id="page_begin_html"></div><script>load_page_begin_html();</script>

<div id="topics">
	<div class = "post">
		<h1 class = "postTitle">
			<a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/frankdeng/p/9529044.html">HBase（十）HBase性能调优总结</a>
		</h1>
		<div class="clear"></div>
		<div class="postBody">
			<div id="cnblogs_post_body" class="blogpost-body"><h2 class="Section11">一. HBase的<strong>通用</strong>优化</h2>
<div class="Section38">
<h3>1&nbsp;高可用</h3>
<p>&nbsp; &nbsp; &nbsp;在&nbsp;HBase&nbsp;中&nbsp;Hmaster&nbsp;负责监控&nbsp;RegionServer&nbsp;的生命周期，均衡&nbsp;RegionServer 的负载，如果&nbsp;Hmaster&nbsp;挂掉了，那么整个&nbsp;HBase 集群将陷入不健康的状态，并且此时的工作状态并不会维持太久。所以&nbsp;HBase&nbsp;支持对&nbsp;Hmaster 的高可用配置。</p>
<p>HBase的高可用集群搭建参考：&nbsp;<a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/frankdeng/p/9310191.html" target="_blank">CentOS7.5搭建HBase1.2.6HA集群</a></p>
</div>
<div class="Section39">
<h3 class="16"><span style="font-size: 15px;">2&nbsp;<strong>Hadoop</strong><strong>&nbsp;</strong>的通用性优化</span></h3>
<p class="16">1)&nbsp;<strong>NameNode</strong><strong>&nbsp;</strong>元数据备份使用&nbsp;<strong>SSD</strong></p>
<p class="16">2)&nbsp;定时备份&nbsp;<strong>NameNode</strong><strong>&nbsp;</strong>上的元数据</p>
<p>每小时或者每天备份，如果数据极其重要，可以&nbsp;5~10 分钟备份一次。备份可以通过定时任务复制元数据目录即可。</p>
<p class="16">3)&nbsp;为&nbsp;<strong>NameNode</strong><strong>&nbsp;</strong>指定多个元数据目录</p>
<p>使用&nbsp;dfs.name.dir&nbsp;或者&nbsp;dfs.namenode.name.dir&nbsp;指定。这样可以提供元数据的冗余和健壮性，&nbsp;以免发生故障。</p>
<p class="16">4)&nbsp;<strong>NameNode</strong><strong>&nbsp;</strong>的&nbsp;<strong>dir</strong><strong>&nbsp;</strong>自恢复</p>
<p>设置&nbsp;dfs.namenode.name.dir.restore&nbsp;为&nbsp;true，允许尝试恢复之前失败的&nbsp;dfs.namenode.name.dir</p>
<p>目录，在创建&nbsp;checkpoint 时做此尝试，如果设置了多个磁盘，建议允许。</p>
<p class="16">5)&nbsp;<strong>HDFS</strong><strong>&nbsp;</strong>保证&nbsp;<strong>RPC</strong><strong>&nbsp;</strong>调用会有较多的线程数</p>
<div class="cnblogs_code">
<pre>属性：dfs.namenode.handler.count
解释：该属性是 NameNode 服务默认线程数，默认值是 10，根据机器的可用内存可以调整为 50~100
属性：dfs.datanode.handler.count
解释：该属性默认值为 10，是 DataNode 的处理线程数，如果 HDFS 客户端程序读写请求比较多，可以调高到 15~20，设置的值越大，内存消耗越多，不要调整的过高，一般业务中，
5~10 即可。</pre>
</div>
<h4><strong>hdfs-site.xml</strong></h4>
</div>
<div class="Section40">
<p class="16">6)&nbsp;<strong>HDFS</strong><strong>&nbsp;</strong>副本数的调整</p>
<div class="cnblogs_code">
<pre>属性：dfs.replication
解释：如果数据量巨大，且不是非常之重要，可以调整为 2~3，如果数据非常之重要，可以调整为 3~5。</pre>
</div>
<h4><strong>hdfs-site.xml</strong></h4>
<p class="16">7)&nbsp;<strong>HDFS</strong><strong>&nbsp;</strong>文件块大小的调整</p>
<div class="cnblogs_code">
<pre>属性：dfs.blocksize
解释：块大小定义，该属性应该根据存储的大量的单个文件大小来设置，如果大量的单个文件都小于 100M，<br />建议设置成 64M 块大小，对于大于 100M 或者达到 GB 的这种情况，建议设置成 256M，一般设置范围波动在 64M~256M 之间。</pre>
</div>
<h4><strong>hdfs-site.xml</strong></h4>
<p class="16">8)&nbsp;<strong>MapReduce</strong><strong>&nbsp;</strong><strong>Job</strong><strong>&nbsp;</strong>任务服务线程数调整</p>
<div class="cnblogs_code">
<pre>属性：mapreduce.jobtracker.handler.count
解释：该属性是 Job 任务线程数，默认值是 10，根据机器的可用内存可以调整为 50~100</pre>
</div>
<h4><strong>mapred-site.xml</strong></h4>
<p class="16">9)&nbsp;<strong>Http</strong><strong>&nbsp;</strong>服务器工作线程数</p>
<h4><strong>mapred-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：mapreduce.tasktracker.http.threads
解释：定义 HTTP 服务器工作线程数，默认值为 40，对于大集群可以调整到 80~100</pre>
</div>
<p class="16">10)&nbsp;文件排序合并优化</p>
<h4><strong>mapred-site.xml</strong></h4>
</div>
<div class="Section41">
<div class="cnblogs_code">
<pre>属性：mapreduce.task.io.sort.factor
解释：文件排序时同时合并的数据流的数量，这也定义了同时打开文件的个数，默认值为
10，如果调高该参数，可以明显减少磁盘 IO，即减少文件读取的次数。</pre>
</div>
<p class="16">11)&nbsp;设置任务并发</p>
<h4><strong>mapred-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：mapreduce.map.speculative
解释：该属性可以设置任务是否可以并发执行，如果任务多而小，该属性设置为 true 可以明显加快任务执行效率，但是对于延迟非常高的任务，建议改为 false，这就类似于迅雷下载。</pre>
</div>
<p class="16">12)&nbsp;<strong>MR</strong><strong>&nbsp;</strong>输出数据的压缩</p>
<h4><strong>mapred-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress
解释：对于大集群而言，建议设置 Map-Reduce 的输出为压缩的数据，而对于小集群，则不需要。</pre>
</div>
<p class="16">13)&nbsp;优化&nbsp;<strong>Mapper</strong><strong>&nbsp;</strong>和&nbsp;<strong>Reducer</strong><strong>&nbsp;</strong>的个数</p>
<h4><strong>mapred-site.xml</strong></h4>
</div>
<div class="Section42">
<div class="cnblogs_code">
<pre>属性：mapreduce.tasktracker.map.tasks.maximum mapreduce.tasktracker.reduce.tasks.maximum
解释：以上两个属性分别为一个单独的 Job 任务可以同时运行的 Map 和 Reduce 的数量。
设置上面两个参数时，需要考虑 CPU 核数、磁盘和内存容量。假设一个 8 核的 CPU，业务内容非常消耗 CPU，那么可以设置 map 数量为 4，如果该业务不是特别消耗 CPU 类型的，<br />那么可以设置 map 数量为 40，reduce 数量为 20。这些参数的值修改完成之后，一定要观察是否有较长等待的任务，如果有的话，可以减少数量以加快任务执行，<br />如果设置一个很大的值，会引起大量的上下文切换，以及内存与磁盘之间的数据交换，这里没有标准的配置数值，
需要根据业务和硬件配置以及经验来做出选择。
在同一时刻，不要同时运行太多的 MapReduce，这样会消耗过多的内存，任务会执行的非常缓慢，我们需要根据 CPU 核数，内存容量设置一个 MR 任务并发的最大值，<br />使固定数据量的任务完全加载到内存中，避免频繁的内存和磁盘数据交换，从而降低磁盘 IO，提高性能。</pre>
</div>
<p>大概估算公式：</p>
<p>map = 2 + ⅔cpu_core，&nbsp; &nbsp; &nbsp; &nbsp;reduce = 2 + ⅓cpu_core</p>
<h3 class="16">3&nbsp;<strong>Linux</strong><strong>&nbsp;</strong>优化</h3>
<p class="16">1)&nbsp;开启文件系统的预读缓存可以提高读取速度</p>
<div class="cnblogs_code">
<pre>$ sudo blockdev --set<span style="color: #ff0000;">ra</span> 32768 /dev/sda</pre>
</div>
<p>尖叫提示：ra&nbsp;是&nbsp;readahead 的缩写</p>
<p class="16">2)&nbsp;关闭进程睡眠池</p>
<p>即不允许后台进程进入睡眠状态，如果进程空闲，则直接&nbsp;kill 掉释放资源</p>
<div class="cnblogs_code">
<pre>$ sudo sysctl -w vm.swappiness=0</pre>
</div>
<p class="16">3)&nbsp;调整&nbsp;<strong>ulimit</strong><strong>&nbsp;</strong>上限，默认值为比较小的数字</p>
<div class="cnblogs_code">
<pre>$ ulimit -n 查看允许最大进程数
$ ulimit -u 查看允许打开最大文件数</pre>
</div>
<p>优化修改：</p>
<table border="0" cellspacing="0">
<tbody>
<tr>
<td valign="top" width="96">
<p class="17">末尾添加：</p>
</td>
<td colspan="3" valign="top" width="237">
<p class="17">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="96">
<p class="17">*</p>
</td>
<td valign="top" width="63">
<p class="17">soft</p>
</td>
<td valign="top" width="81">
<p class="17">nofile</p>
</td>
<td valign="top" width="92">
<p class="17" align="right">1024000</p>
</td>
</tr>
<tr>
<td valign="top" width="96">
<p class="17">*</p>
</td>
<td valign="top" width="63">
<p class="17">hard</p>
</td>
<td valign="top" width="81">
<p class="17">nofile</p>
</td>
<td valign="top" width="92">
<p class="17" align="right">1024000</p>
</td>
</tr>
<tr>
<td valign="top" width="96">
<p class="17">Hive</p>
</td>
<td valign="top" width="63">
<p class="17">-</p>
</td>
<td valign="top" width="81">
<p class="17">nofile</p>
</td>
<td valign="top" width="92">
<p class="17" align="right">1024000</p>
</td>
</tr>
<tr>
<td valign="top" width="96">
<p class="17">hive</p>
</td>
<td valign="top" width="63">
<p class="17">-</p>
</td>
<td valign="top" width="81">
<p class="17">nproc</p>
</td>
<td valign="top" width="92">
<p class="17" align="right">1024000</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="Section43">
<p class="16">4)&nbsp;开启集群的时间同步&nbsp;<strong>NTP</strong></p>
<p>集群中某台机器同步网络时间服务器的时间，集群中其他机器则同步这台机器的时间。</p>
<p class="16">5)&nbsp;更新系统补丁</p>
<p>更新补丁前，请先测试新版本补丁对集群节点的兼容性。</p>
<h3 class="16">4&nbsp;<strong>Zookeeper</strong><strong>&nbsp;</strong>优化</h3>
<p><strong>1)&nbsp;</strong>优化&nbsp;<strong>Zookeeper&nbsp;</strong>会话超时时间</p>
<h4><strong>hbase-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>参数：zookeeper.session.timeout
解 释 ：In hbase-site.xml, set zookeeper.session.timeout to 30 seconds or less to bound failure detection (20-30 seconds is a good start).<br />该值会直接关系到 master 发现服务器宕机的最大周期，默认值为 30 秒，如果该值过小，会在 HBase 在写入大量数据发生而 GC 时，导致RegionServer 短暂的不可用，<br />从而没有向 ZK 发送心跳包，最终导致认为从节点 shutdown。<span style="color: #ff0000;">一般 20 台左右的集群需要配置 5 台 zookeeper</span>。</pre>
</div>
<h2 class="16">二.&nbsp;<strong>HBase的个性</strong>优化</h2>
<h3>1&nbsp;预分区及<strong>RowKey</strong>设计</h3>
<p align="justify">详细请看：<a id="link_post_title" class="link-post-title" href="https://www.cnblogs.com/frankdeng/p/9310356.html" target="_blank">HBase表以及Rowkey的设计原则</a></p>
</div>
<div class="Section45">
<h3>2&nbsp;内存优化</h3>
<p align="justify">HBase&nbsp;操作过程中需要大量的内存开销，毕竟&nbsp;Table 是可以缓存在内存中的，一般会分配整个可用内存的&nbsp;70%给&nbsp;HBase&nbsp;的&nbsp;Java&nbsp;堆。但是不建议分配非常大的堆内存，因为&nbsp;GC 过程持续太久会导致&nbsp;RegionServer&nbsp;处于长期不可用状态，一般&nbsp;16~48G 内存就可以了，如果因为框架占用内存过高导致系统内存不足，框架一样会被系统服务拖死。</p>
<h3 align="justify">3&nbsp; 基础优化</h3>
<p class="16" align="justify">1)&nbsp;允许在&nbsp;<strong>HDFS</strong><strong>&nbsp;</strong>的文件中追加内容</p>
<p align="justify">不是不允许追加内容么？没错，请看背景故事：<a href="http://blog.cloudera.com/blog/2009/07/file-appends-in-hdfs/" target="_blank">http://blog.cloudera.com/blog/2009/07/file-appends-in-hdfs/</a></p>
<h4><strong>hdfs-site.xml</strong>、<strong>hbase-site.xml</strong></h4>
</div>
<div class="Section46">
<div class="cnblogs_code">
<pre>属性：dfs.support.append
解释：开启 HDFS 追加同步，可以优秀的配合 HBase 的数据同步和持久化。默认值为 true.</pre>
</div>
<p class="16">2)&nbsp;优化&nbsp;<strong>DataNode</strong><strong>&nbsp;</strong>允许的最大文件打开数</p>
<h4><strong>hdfs-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：dfs.datanode.max.transfer.threads
解释：HBase 一般都会同一时间操作大量的文件，根据集群的数量和规模以及数据动作，设置为 4096 或者更高。默认值：4096</pre>
</div>
<p class="16">3)&nbsp;优化延迟高的数据操作的等待时间</p>
<h4><strong>hdfs-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：dfs.image.transfer.timeout
解释：如果对于某一次数据操作来讲，延迟非常高，socket 需要等待更长的时间，建议把该值设置为更大的值（默认 60000 毫秒），以确保 socket 不会被 timeout 掉。</pre>
</div>
<p class="16">4)&nbsp;优化数据的写入效率</p>
<h4><strong>mapred-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：
mapreduce.map.output.compress    mapreduce.map.output.compress.codec
解释：开启这两个数据可以大大提高文件的写入效率，减少写入时间。第一个属性值修改为true，第二个属性值修改为：org.apache.hadoop.io.compress.GzipCodec 或者其他压缩方式</pre>
</div>
<p class="16">5)&nbsp;优化&nbsp;<strong>DataNode</strong><strong>&nbsp;</strong>存储</p>
<div class="cnblogs_code">
<pre>属性：dfs.datanode.failed.volumes.tolerated
解释：默认为 0，意思是当 DataNode 中有一个磁盘出现故障，则会认为该 DataNode shutdown 了。<br />如果修改为 1，则一个磁盘出现故障时，数据会被复制到其他正常的 DataNode 上，当前的 DataNode 继续工作。</pre>
</div>
<p class="16">6)&nbsp;设置&nbsp;<strong>RPC</strong><strong>&nbsp;</strong>监听数量</p>
<h4><strong>hbase-site.xml</strong></h4>
</div>
<div class="Section47">
<div class="cnblogs_code">
<pre>属性：hbase.regionserver.handler.count
解释：默认值为 30，用于指定 RPC 监听的数量，可以根据客户端的请求数进行调整，<span style="color: #ff0000;">读写请求较多时，增加此值</span>。</pre>
</div>
<p class="16">7)&nbsp;优化&nbsp;<strong>HStore</strong><strong>&nbsp;</strong>文件大小</p>
<h4><strong>hbase-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：hbase.hregion.max.filesize
解释：默认值 10737418240（<span style="color: #ff0000;">10GB</span>），如果需要运行 HBase 的 MR 任务，可以减小此值， 因为一个 region 对应一个 map 任务,<br />如果单个 region 过大，会导致 map 任务执行时间过长。该值的意思就是，如果 HFile 的大小达到这个数值，则这个 region 会被切分为两个 Hfile。</pre>
</div>
<p class="16">8)&nbsp;优化&nbsp;<strong>hbase</strong><strong>&nbsp;</strong>客户端缓存</p>
<h4><strong>hbase-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：hbase.client.write.buffer
解释：用于指定 HBase 客户端缓存，增大该值可以减少 RPC 调用次数，但是会消耗更多内存，反之则反之。一般我们需要设定一定的缓存大小，以达到减少 RPC 次数的目的。</pre>
</div>
<p class="16">9)&nbsp;指定&nbsp;<strong>scan.next</strong><strong>&nbsp;</strong>扫描&nbsp;<strong>HBase</strong><strong>&nbsp;</strong>所获取的行数</p>
<h4><strong>hbase-site.xml</strong></h4>
<div class="cnblogs_code">
<pre>属性：hbase.client.scanner.caching
解释：用于指定 scan.next 方法获取的默认行数，值越大，消耗内存越大。</pre>
</div>
<p class="16">10)&nbsp;<strong>flush</strong>、<strong>compact</strong>、<strong>split</strong><strong>&nbsp;</strong>机制</p>
<p align="justify">当&nbsp;MemStore&nbsp;达到阈值，将&nbsp;Memstore&nbsp;中的数据&nbsp;Flush&nbsp;进&nbsp;Storefile；compact&nbsp;机制则是把&nbsp;flush&nbsp;出来的小文件合并成大的&nbsp;Storefile&nbsp;文件。split&nbsp;则是当&nbsp;Region&nbsp;达到阈值，会把过大的&nbsp;Region 一分为二。</p>
<p align="justify">涉及属性：</p>
<p align="justify">即：<span style="color: #ff0000;">128M</span>&nbsp;就是&nbsp;Memstore 的默认阈值</p>
</div>
<div class="cnblogs_code">
<pre>hbase.hregion.memstore.flush.size：134217728</pre>
</div>
<div class="Section48">
<p>即：这个参数的作用是当单个&nbsp;HRegion&nbsp;内所有的&nbsp;Memstore 大小总和超过指定值时，flush</p>
<p align="justify">该&nbsp;HRegion&nbsp;的所有&nbsp;memstore。RegionServer&nbsp;的&nbsp;flush 是通过将请求添加一个队列，模拟生产消费模型来异步处理的。那这里就有一个问题，当队列来不及消费，产生大量积压请求时，可能会导致内存陡增，最坏的情况是触发&nbsp;OOM。</p>
<div class="cnblogs_code">
<pre>hbase.regionserver.global.memstore.upperLimit：0.4 <br />hbase.regionserver.global.memstore.lowerLimit：0.38</pre>
</div>
<p>即：当&nbsp;MemStore&nbsp;使用内存总量达到&nbsp;hbase.regionserver.global.memstore.upperLimit 指定值时，将会有多个&nbsp;MemStores flush 到文件中，MemStore flush&nbsp;顺序是按照大小降序执行的，直到刷新到&nbsp;MemStore&nbsp;使用内存略小于&nbsp;lowerLimit</p>
<h2 class="16">三.&nbsp;<strong>HBase的写表</strong>优化</h2>
<h3 id="blogTitle9"><strong>1&nbsp;多HTable并发写</strong></h3>
<p class="p">创建多个HTable客户端用于写操作，提高写数据的吞吐量，一个例子：</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">static</span> final Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
</span><span style="color: #0000ff;">static</span> final String table_log_name =<span style="color: #000000;"> &ldquo;user_log&rdquo;;
wTableLog </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> HTable[tableN];
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; tableN; i++<span style="color: #000000;">) {
    wTableLog[i] </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> HTable(conf, table_log_name);
    wTableLog[i].setWriteBufferSize(</span><span style="color: #800080;">5</span> * <span style="color: #800080;">1024</span> * <span style="color: #800080;">1024</span>); <span style="color: #008000;">//</span><span style="color: #008000;">5MB</span>
    wTableLog[i].setAutoFlush(<span style="color: #0000ff;">false</span><span style="color: #000000;">);
}</span></pre>
</div>
<h3 id="blogTitle10"><strong>2&nbsp; HTable参数设置</strong></h3>
<h4 id="blogTitle11"><strong>Auto Flush</strong></h4>
<p class="p">通过调用HTable.setAutoFlush(false)方法可以将HTable写客户端的自动flush关闭，这样可以批量写入数据到HBase，而不是有一条put就执行一次更新，只有当put填满客户端写缓存时，才实际向HBase服务端发起写请求。默认情况下auto flush是开启的。</p>
<h4 id="blogTitle12"><strong>Write Buffer</strong></h4>
<p class="p">通过调用HTable.setWriteBufferSize(writeBufferSize)方法可以设置HTable客户端的写buffer大小，如果新设置的buffer小于当前写buffer中的数据时，buffer将会被flush到服务端。其中，writeBufferSize的单位是byte字节数，可以根据实际写入数据量的多少来设置该值。</p>
<h4 id="blogTitle13"><strong>WAL Flag</strong></h4>
<p class="p">在HBae中，客户端向集群中的RegionServer提交数据时（Put/Delete操作），首先会先写WAL（Write Ahead Log）日志（即HLog，一个RegionServer上的所有Region共享一个HLog），只有当WAL日志写成功后，再接着写MemStore，然后客户端被通知提交数据成功；如果写WAL日志失败，客户端则被通知提交失败。这样做的好处是可以做到RegionServer宕机后的数据恢复。</p>
<p class="p">因此，对于相对不太重要的数据，可以在Put/Delete操作时，通过调用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函数，放弃写WAL日志，从而提高数据写入的性能。</p>
<p class="p"><strong>值得注意的是：谨慎选择关闭WAL日志，因为这样的话，一旦RegionServer宕机，Put/Delete的数据将会无法根据WAL日志进行恢复。</strong></p>
<h3 id="blogTitle14"><strong>3&nbsp;批量写</strong></h3>
<p class="p">通过调用HTable.put(Put)方法可以将一个指定的row key记录写入HBase，同样HBase提供了另一个方法：通过调用HTable.put(List&lt;Put&gt;)方法可以将指定的row key列表，批量写入多行记录，这样做的好处是批量执行，只需要一次网络I/O开销，这对于对数据实时性要求高，网络传输RTT高的情景下可能带来明显的性能提升。</p>
<h3 id="blogTitle15"><strong>4&nbsp;</strong><strong>多线程并发写</strong></h3>
<p class="p">在客户端开启多个HTable写线程，每个写线程负责一个HTable对象的flush操作，这样结合定时flush和写buffer（writeBufferSize），可以既保证在数据量小的时候，数据可以在较短时间内被flush（如1秒内），同时又保证在数据量大的时候，写buffer一满就及时进行flush。下面给个具体的例子：</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; threadN; i++<span style="color: #000000;">) {
    Thread th </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread() {
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
            </span><span style="color: #0000ff;">while</span> (<span style="color: #0000ff;">true</span><span style="color: #000000;">) {
                </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                    sleep(</span><span style="color: #800080;">1000</span>); <span style="color: #008000;">//</span><span style="color: #008000;">1 second</span>
                } <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
                    e.printStackTrace();
                }
synchronized (wTableLog[i]) {
                    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                        wTableLog[i].flushCommits();
                    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
}
    };
    th.setDaemon(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);
    th.start();
}</span></pre>
</div>
<h2 class="16">四.&nbsp;<strong>HBase的读表</strong>优化</h2>
<h3 id="blogTitle17"><strong>1&nbsp;多HTable并发读</strong></h3>
<p class="p">创建多个HTable客户端用于读操作，提高读数据的吞吐量，一个例子：</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">static</span> final Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
</span><span style="color: #0000ff;">static</span> final String table_log_name =<span style="color: #000000;"> &ldquo;user_log&rdquo;;<br />
rTableLog </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> HTable[tableN];
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; tableN; i++<span style="color: #000000;">) {
    rTableLog[i] </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> HTable(conf, table_log_name);
    rTableLog[i].setScannerCaching(</span><span style="color: #800080;">50</span><span style="color: #000000;">);
}</span></pre>
</div>
<h3 id="blogTitle18"><strong>2&nbsp; HTable参数设置</strong></h3>
<h4 id="blogTitle19"><strong>Scanner Caching</strong></h4>
<p class="p">hbase.client.scanner.caching配置项可以设置HBase scanner一次从服务端抓取的数据条数，默认情况下一次一条。通过将其设置成一个合理的值，可以减少scan过程中next()的时间开销，代价是scanner需要通过客户端的内存来维持这些被cache的行记录。</p>
<p class="p">有三个地方可以进行配置：<strong>三者的优先级越来越高。</strong></p>
<p class="p">1）在HBase的conf配置文件中进行配置；</p>
<p class="p">2）通过调用HTable.setScannerCaching(int scannerCaching)进行配置；</p>
<p class="p">3）通过调用Scan.setCaching(int caching)进行配置。</p>
<h4 id="blogTitle20"><strong>Scan Attribute Selection</strong></h4>
<p class="pre">scan时指定需要的Column Family，可以减少网络传输数据量，否则默认scan操作会返回整行所有Column Family的数据。</p>
<h4 id="blogTitle21"><strong>Close ResultScanner</strong></h4>
<p class="pre">通过scan取完数据后，记得要关闭ResultScanner，否则RegionServer可能会出现问题（对应的Server资源无法释放）。</p>
<h3 id="blogTitle22"><strong>3&nbsp;批量读</strong></h3>
<p class="p">通过调用HTable.get(Get)方法可以根据一个指定的row key获取一行记录，同样HBase提供了另一个方法：通过调用HTable.get(List&lt;Get&gt;)方法可以根据一个指定的row key列表，批量获取多行记录，这样做的好处是批量执行，只需要一次网络I/O开销，这对于对数据实时性要求高而且网络传输RTT高的情景下可能带来明显的性能提升。</p>
<h3 id="blogTitle23"><strong>4&nbsp;多线程并发读</strong></h3>
<p class="p">在客户端开启多个HTable读线程，每个读线程负责通过HTable对象进行get操作。下面是一个多线程并发读取HBase，获取店铺一天内各分钟PV值的例子：</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> DataReaderServer {
     </span><span style="color: #008000;">//</span><span style="color: #008000;">获取店铺一天内各分钟PV值的入口函数</span>
     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> ConcurrentHashMap&lt;String, String&gt; getUnitMinutePV(<span style="color: #0000ff;">long</span> uid, <span style="color: #0000ff;">long</span> startStamp, <span style="color: #0000ff;">long</span><span style="color: #000000;"> endStamp){
         </span><span style="color: #0000ff;">long</span> min =<span style="color: #000000;"> startStamp;
         </span><span style="color: #0000ff;">int</span> count = (<span style="color: #0000ff;">int</span>)((endStamp - startStamp) / (<span style="color: #800080;">60</span>*<span style="color: #800080;">1000</span><span style="color: #000000;">));
         List</span>&lt;String&gt; lst = <span style="color: #0000ff;">new</span> ArrayList&lt;String&gt;<span style="color: #000000;">();
         </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt;= count; i++<span style="color: #000000;">) {
            min </span>= startStamp + i * <span style="color: #800080;">60</span> * <span style="color: #800080;">1000</span><span style="color: #000000;">;
            lst.add(uid </span>+ <span style="color: #800000;">"</span><span style="color: #800000;">_</span><span style="color: #800000;">"</span> +<span style="color: #000000;"> min);
         }
         </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> parallelBatchMinutePV(lst);
     }
      </span><span style="color: #008000;">//</span><span style="color: #008000;">多线程并发查询，获取分钟PV值</span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> ConcurrentHashMap&lt;String, String&gt; parallelBatchMinutePV(List&lt;String&gt;<span style="color: #000000;"> lstKeys){
        ConcurrentHashMap</span>&lt;String, String&gt; hashRet = <span style="color: #0000ff;">new</span> ConcurrentHashMap&lt;String, String&gt;<span style="color: #000000;">();
        </span><span style="color: #0000ff;">int</span> parallel = <span style="color: #800080;">3</span><span style="color: #000000;">;
        List</span>&lt;List&lt;String&gt;&gt; lstBatchKeys  = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span> (lstKeys.size() &lt;<span style="color: #000000;"> parallel ){
            lstBatchKeys  </span>= <span style="color: #0000ff;">new</span> ArrayList&lt;List&lt;String&gt;&gt;(<span style="color: #800080;">1</span><span style="color: #000000;">);
            lstBatchKeys.add(lstKeys);
        }
        </span><span style="color: #0000ff;">else</span><span style="color: #000000;">{
            lstBatchKeys  </span>= <span style="color: #0000ff;">new</span> ArrayList&lt;List&lt;String&gt;&gt;<span style="color: #000000;">(parallel);
            </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; parallel; i++<span style="color: #000000;">  ){
                List</span>&lt;String&gt; lst = <span style="color: #0000ff;">new</span> ArrayList&lt;String&gt;<span style="color: #000000;">();
                lstBatchKeys.add(lst);
            }

            </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span> ; i &lt; lstKeys.size() ; i ++<span style="color: #000000;"> ){
                lstBatchKeys.</span><span style="color: #0000ff;">get</span>(i%parallel).add(lstKeys.<span style="color: #0000ff;">get</span><span style="color: #000000;">(i));
            }
        }
        
        List</span>&lt;Future&lt; ConcurrentHashMap&lt;String, String&gt; &gt;&gt; futures = <span style="color: #0000ff;">new</span> ArrayList&lt;Future&lt; ConcurrentHashMap&lt;String, String&gt; &gt;&gt;(<span style="color: #800080;">5</span><span style="color: #000000;">);
        
        ThreadFactoryBuilder builder </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> ThreadFactoryBuilder();
        builder.setNameFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">ParallelBatchQuery</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        ThreadFactory factory </span>=<span style="color: #000000;"> builder.build();
        ThreadPoolExecutor executor </span>=<span style="color: #000000;"> (ThreadPoolExecutor) Executors.newFixedThreadPool(lstBatchKeys.size(), factory);
        
        </span><span style="color: #0000ff;">for</span>(List&lt;String&gt;<span style="color: #000000;"> keys : lstBatchKeys){
            Callable</span>&lt; ConcurrentHashMap&lt;String, String&gt; &gt; callable = <span style="color: #0000ff;">new</span><span style="color: #000000;"> BatchMinutePVCallable(keys);
            FutureTask</span>&lt; ConcurrentHashMap&lt;String, String&gt; &gt; future = (FutureTask&lt; ConcurrentHashMap&lt;String, String&gt; &gt;<span style="color: #000000;">) executor.submit(callable);
            futures.add(future);
        }
        executor.shutdown();
        
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Wait for all the tasks to finish</span>
        <span style="color: #0000ff;">try</span><span style="color: #000000;"> {
          boolean stillRunning </span>= !<span style="color: #000000;">executor.awaitTermination(
              </span><span style="color: #800080;">5000000</span><span style="color: #000000;">, TimeUnit.MILLISECONDS);
          </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (stillRunning) {
            </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                executor.shutdownNow();
            } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> TODO Auto-generated catch block</span>
<span style="color: #000000;">                e.printStackTrace();
            }
          }
        } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
          </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
              Thread.currentThread().interrupt();
          } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e1) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> TODO Auto-generated catch block</span>
<span style="color: #000000;">            e1.printStackTrace();
          }
        }
        
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Look for any exception</span>
        <span style="color: #0000ff;">for</span><span style="color: #000000;"> (Future f : futures) {
          </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
              </span><span style="color: #0000ff;">if</span>(f.<span style="color: #0000ff;">get</span>() != <span style="color: #0000ff;">null</span><span style="color: #000000;">)
              {
                  hashRet.putAll((ConcurrentHashMap</span>&lt;String, String&gt;)f.<span style="color: #0000ff;">get</span><span style="color: #000000;">());
              }
          } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
            </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                 Thread.currentThread().interrupt();
            } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e1) {
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> TODO Auto-generated catch block</span>
<span style="color: #000000;">                e1.printStackTrace();
            }
          } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (ExecutionException e) {
            e.printStackTrace();
          }
        }
        
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> hashRet;
    }
     </span><span style="color: #008000;">//</span><span style="color: #008000;">一个线程批量查询，获取分钟PV值</span>
    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">static</span> ConcurrentHashMap&lt;String, String&gt; getBatchMinutePV(List&lt;String&gt;<span style="color: #000000;"> lstKeys){
        ConcurrentHashMap</span>&lt;String, String&gt; hashRet = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        List</span>&lt;Get&gt; lstGet = <span style="color: #0000ff;">new</span> ArrayList&lt;Get&gt;<span style="color: #000000;">();
        String[] splitValue </span>= <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String s : lstKeys) {
            splitValue </span>= s.split(<span style="color: #800000;">"</span><span style="color: #800000;">_</span><span style="color: #800000;">"</span><span style="color: #000000;">);
            </span><span style="color: #0000ff;">long</span> uid = Long.parseLong(splitValue[<span style="color: #800080;">0</span><span style="color: #000000;">]);
            </span><span style="color: #0000ff;">long</span> min = Long.parseLong(splitValue[<span style="color: #800080;">1</span><span style="color: #000000;">]);
            </span><span style="color: #0000ff;">byte</span>[] key = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">byte</span>[<span style="color: #800080;">16</span><span style="color: #000000;">];
            Bytes.putLong(key, </span><span style="color: #800080;">0</span><span style="color: #000000;">, uid);
            Bytes.putLong(key, </span><span style="color: #800080;">8</span><span style="color: #000000;">, min);
            Get g </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Get(key);
            g.addFamily(fp);
            lstGet.add(g);
        }
        Result[] res </span>= <span style="color: #0000ff;">null</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            res </span>= tableMinutePV[rand.nextInt(tableN)].<span style="color: #0000ff;">get</span><span style="color: #000000;">(lstGet);
        } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (IOException e1) {
            logger.error(</span><span style="color: #800000;">"</span><span style="color: #800000;">tableMinutePV exception, e=</span><span style="color: #800000;">"</span> +<span style="color: #000000;"> e1.getStackTrace());
        }

        </span><span style="color: #0000ff;">if</span> (res != <span style="color: #0000ff;">null</span> &amp;&amp; res.length &gt; <span style="color: #800080;">0</span><span style="color: #000000;">) {
            hashRet </span>= <span style="color: #0000ff;">new</span> ConcurrentHashMap&lt;String, String&gt;<span style="color: #000000;">(res.length);
            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (Result re : res) {
                </span><span style="color: #0000ff;">if</span> (re != <span style="color: #0000ff;">null</span> &amp;&amp; !<span style="color: #000000;">re.isEmpty()) {
                    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                        </span><span style="color: #0000ff;">byte</span>[] key =<span style="color: #000000;"> re.getRow();
                        </span><span style="color: #0000ff;">byte</span>[] value =<span style="color: #000000;"> re.getValue(fp, cp);
                        </span><span style="color: #0000ff;">if</span> (key != <span style="color: #0000ff;">null</span> &amp;&amp; value != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
                            hashRet.put(String.valueOf(Bytes.toLong(key,
                                    Bytes.SIZEOF_LONG)), String.valueOf(Bytes
                                    .toLong(value)));
                        }
                    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e2) {
                        logger.error(e2.getStackTrace());
                    }
                }
            }
        }

        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> hashRet;
    }
}
</span><span style="color: #008000;">//</span><span style="color: #008000;">调用接口类，实现Callable接口</span>
<span style="color: #0000ff;">class</span> BatchMinutePVCallable implements Callable&lt;ConcurrentHashMap&lt;String, String&gt;&gt;<span style="color: #000000;">{
     </span><span style="color: #0000ff;">private</span> List&lt;String&gt;<span style="color: #000000;"> keys;

     </span><span style="color: #0000ff;">public</span> BatchMinutePVCallable(List&lt;String&gt;<span style="color: #000000;"> lstKeys ) {
         </span><span style="color: #0000ff;">this</span>.keys =<span style="color: #000000;"> lstKeys;
     }

     </span><span style="color: #0000ff;">public</span> ConcurrentHashMap&lt;String, String&gt;<span style="color: #000000;"> call() throws Exception {
         </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> DataReadServer.getBatchMinutePV(keys);
     }
}</span></pre>
</div>
<h3 id="blogTitle24"><strong>5&nbsp;缓存查询结果</strong></h3>
<p class="p">对于频繁查询HBase的应用场景，可以考虑在应用程序中做缓存，当有新的查询请求时，首先在缓存中查找，如果存在则直接返回，不再查询HBase；否则对HBase发起读请求查询，然后在应用程序中将查询结果缓存起来。至于缓存的替换策略，可以考虑<span style="color: #ff0000;">LRU</span>等常用的策略。</p>
<h3 id="blogTitle25"><strong>6&nbsp;</strong><strong>Blockcache</strong></h3>
<p class="p">HBase上Regionserver的内存分为两个部分，一部分作为Memstore，主要用来写；另外一部分作为BlockCache，主要用于读。</p>
<p class="p">写请求会先写入Memstore，Regionserver会给每个region提供一个Memstore，当Memstore满64MB以后，会启动&nbsp;flush刷新到磁盘。当Memstore的总大小超过限制时（heapsize * hbase.regionserver.global.memstore.upperLimit * 0.9），会强行启动flush进程，从最大的Memstore开始flush直到低于限制。</p>
<p class="p">读请求先到Memstore中查数据，查不到就到BlockCache中查，再查不到就会到磁盘上读，并把读的结果放入BlockCache。由于BlockCache采用的是LRU策略，因此BlockCache达到上限(heapsize * hfile.block.cache.size * 0.85)后，会启动淘汰机制，淘汰掉最老的一批数据。</p>
<p class="p">一个Regionserver上有一个BlockCache和N个Memstore，它们的大小之和不能大于等于heapsize * 0.8，否则HBase不能启动。默认BlockCache为0.2，而Memstore为0.4。<strong>对于注重读响应时间的系统，可以将</strong><strong>&nbsp;</strong><strong>BlockCache设大些，比如设置BlockCache=0.4，Memstore=0.39，以加大缓存的命中率。</strong></p>
<p class="p">有关BlockCache机制，请参考这里：<a href="http://punishzhou.iteye.com/blog/1277141" target="_blank">HBase的Block cache</a>，<a href="http://www.cnblogs.com/raymondshiquan/archive/2011/07/24/2115397.html" target="_blank">HBase的blockcache机制</a>，<a href="http://koven2049.iteye.com/blog/1002135" target="_blank">hbase中的缓存的计算与使用</a>。</p>
<h2><strong>五.&nbsp;HTable与</strong><strong>HTable&nbsp;</strong><strong>Pool</strong></h2>
<p><a href="http://www.cnblogs.com/panfeng412/archive/2012/07/11/htable-and-htablepool-apply-notes.html" target="_blank">HTable和HTablePool使用注意事项</a></p>
<p class="p">HTable和HTablePool都是HBase客户端API的一部分，可以使用它们对HBase表进行CRUD操作。下面结合在项目中的应用情况，对二者使用过程中的注意事项做一下概括总结。</p>
<div class="cnblogs_code">
<pre>Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
</span><span style="color: #0000ff;">try</span> (Connection connection =<span style="color: #000000;"> ConnectionFactory.createConnection(conf)) {
  </span><span style="color: #0000ff;">try</span> (Table table =<span style="color: #000000;"> connection.getTable(TableName.valueOf(tablename)) {
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> use table as needed, the table returned is lightweight</span><span style="color: #000000;">
  }
}</span></pre>
</div>
<h3><strong>1.&nbsp;</strong><strong>HTable</strong></h3>
<p class="p">HTable是HBase客户端与HBase服务端通讯的Java API对象，客户端可以通过HTable对象与服务端进行CRUD操作（增删改查）。它的创建很简单：</p>
<div class="cnblogs_code">
<pre>Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
HTable table </span>= <span style="color: #0000ff;">new</span> HTable(conf, <span style="color: #800000;">"</span><span style="color: #800000;">tablename</span><span style="color: #800000;">"</span><span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;">TODO CRUD Operation&hellip;&hellip;</span></pre>
</div>
<h4 class="p"><strong>HTable使用时的一些注意事项：</strong></h4>
<h4 id="blogTitle27" class="p"><strong>1.&nbsp;&nbsp;</strong><strong>&nbsp;</strong><strong>规避HTable对象的创建开销</strong></h4>
<p class="p">因为客户端创建HTable对象后，需要进行一系列的操作：检查.META.表确认指定名称的HBase表是否存在，表是否有效等等，整个时间开销比较重，可能会耗时几秒钟之长，因此最好在程序启动时一次性创建完成需要的HTable对象，如果使用Java API，一般来说是在构造函数中进行创建，程序启动后直接重用。</p>
<h4 id="blogTitle28" class="p"><strong>2.&nbsp;&nbsp;</strong><strong>&nbsp;</strong><strong>HTable对象不是线程安全的</strong></h4>
<p class="p">HTable对象对于客户端读写数据来说不是线程安全的，因此多线程时，要为每个线程单独创建复用一个HTable对象，不同对象间不要共享HTable对象使用，特别是在客户端auto flash被置为false时，由于存在本地write buffer，可能导致数据不一致。</p>
<h4 id="blogTitle29" class="p"><strong>3.&nbsp;&nbsp;</strong><strong>&nbsp;</strong><strong>HTable对象之间共享Configuration</strong></h4>
<p class="p">HTable对象共享Configuration对象，这样的好处在于：</p>
<ul>
<li>共享ZooKeeper的连接：每个客户端需要与ZooKeeper建立连接，查询用户的table regions位置，这些信息可以在连接建立后缓存起来共享使用；</li>
<li>共享公共的资源：客户端需要通过ZooKeeper查找-ROOT-和.META.表，这个需要网络传输开销，客户端缓存这些公共资源后能够减少后续的网络传输开销，加快查找过程速度。</li>
</ul>
<p class="p">因此，与以下这种方式相比：</p>
<p class="pre">HTable table1 = new&nbsp;HTable("table1");</p>
<p class="pre">HTable table2 = new&nbsp;HTable("table2");</p>
<p class="p">下面的方式更有效些：</p>
<div class="cnblogs_code">
<pre>Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
HTable table1 </span>= <span style="color: #0000ff;">new</span> HTable(conf, <span style="color: #800000;">"</span><span style="color: #800000;">table1</span><span style="color: #800000;">"</span><span style="color: #000000;">);
HTable table2 </span>= <span style="color: #0000ff;">new</span> HTable(conf, <span style="color: #800000;">"</span><span style="color: #800000;">table2</span><span style="color: #800000;">"</span>);</pre>
</div>
<p class="p">备注：即使是高负载的多线程程序，也并没有发现因为共享Configuration而导致的性能问题；如果你的实际情况中不是如此，那么可以尝试不共享Configuration。</p>
<h3 id="blogTitle30"><strong>2.&nbsp; HTable&nbsp; Pool</strong></h3>
<p class="p">HTablePool可以解决HTable存在的线程不安全问题，同时通过维护固定数量的HTable对象，能够在程序运行期间复用这些HTable资源对象。</p>
<div class="cnblogs_code">
<pre>Configuration conf =<span style="color: #000000;"> HBaseConfiguration.create();
HTablePool pool </span>= <span style="color: #0000ff;">new</span> HTablePool(conf, <span style="color: #800080;">10</span>);</pre>
</div>
<p class="p">1.&nbsp;&nbsp; HTablePool可以自动创建HTable对象，而且对客户端来说使用上是完全透明的，可以避免多线程间数据并发修改问题。</p>
<p class="p">2.&nbsp;&nbsp; HTablePool中的HTable对象之间是公用Configuration连接的，能够可以减少网络开销。</p>
<p class="p">HTablePool的使用很简单：每次进行操作前，通过HTablePool的getTable方法取得一个HTable对象，然后进行put/get/scan/delete等操作，最后通过HTablePool的putTable方法将HTable对象放回到HTablePool中。</p>
<p class="p">下面是个使用HTablePool的简单例子：</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> createUser(String username, String firstName, String lastName, String email, String password, String roles) throws IOException {

　　HTable table </span>=<span style="color: #000000;"> rm.getTable(UserTable.NAME);
　　Put put </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Put(Bytes.toBytes(username));
　　put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME,
　　Bytes.toBytes(firstName));
　　put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME,Bytes.toBytes(lastName));
　　put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email));
　　put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS,Bytes.toBytes(password));
　　put.add(UserTable.DATA_FAMILY, UserTable.ROLES, Bytes.toBytes(roles));
　　table.put(put);
　　table.flushCommits();
　　rm.putTable(table);
}</span></pre>
</div>
<p>HBase和DBMS比较：</p>
<p>查询数据不灵活：</p>
<p class="24">1、&nbsp;不能使用column之间过滤查询</p>
<p class="24">2、&nbsp;不支持全文索引。使用solr和hbase整合完成全文搜索。</p>
<p class="24">a)&nbsp;使用MR批量读取hbase中的数据，在solr里面建立索引（no &nbsp;store）之保存rowkey的值。</p>
<p class="24">b)&nbsp;根据关键词从索引中搜索到rowkey（分页）</p>
<p class="24">c)&nbsp;根据rowkey从hbase查询所有数据</p>
</div></div>
</body>
</html>
